路遙知碼力,日久練成精-只要在程式之路鑽研的夠深,便能夠充分發揮程式碼的力量; 練習的日子夠久,便能夠練成寫出精簡代碼的能力。
先公佈一下昨天課後練習的解答吧:
(還沒看過題目的朋友歡迎點昨日題目傳送門)
def checkRepeat(plays):
return len(set(plays))<len(plays)
想法就是說如果去除重複元素之後,
原始列表的元素有變少,
那麼原始列表就真的有重複元素,
你有答對嗎?
有其它解法也歡迎在留言區分享哦。
之前我們比喻「切片」與「列表生成式」就像是Python世界中的「屠龍寶刀」,
雖然簡單但又非常實用,
今天終於要傳授第二屠龍刀-列表生成式了。
列表生成式(List Comprehensions),顧名思義
,為運用一個列表生出另一個列表的語法。
基礎語法如下:
新列表 = [something for x in 舊列表(或可迭代物件)]
相當於
新列表 = []
for x in 舊列表(或可迭代物件):
新列表.append(something)
這樣的寫法。
在列表生成式語法中,
直接把for迴圈放進列表中以簡化程式碼,
我們直接看一個情境了解列表生成式如何使用。
某高中數學老師,因為班上期末考的平均分數過低,
決定幫同學們調分。
調分方式為將班上最高分的同學加到100分,
再將其它同學加上等量的分數。
例如有兩位同學分別考了30分及45分,
而全班最高分的同學考了80分。
老師便會將最高分的同學加20分成為100分,
再把30分及45分的同學各自加上20分,
變成50分及65分。
給定一個列表表示班上同學的原始分數,
例如:
scores = [20,30,50,60,25,70]
計算出班上調分之後的結果。
我們先思考一下如果不用列表生成式可以怎麼解。
首先我們先宣告一個新的列表用來存放新的分數:
new_scores = []
再來,我們用一個變數adjust
記錄需要往上加多少分,
調分 = 100 - 全班最高分:
adjust = 100 - max(scores)
我們用for迴圈遍歷,
將原始分數加上調分後,
再依序放到新列表中,
完整程式碼如下:
scores = [20,30,50,60,25,70]
new_scores = []
adjust = 100 - max(scores)
for s in scores:
new_scores.append(s+adjust)
print(new_scores)
結果為 [50, 60, 80, 90, 55, 100]
。
但是我們可以使用列表生成式,
把for迴圈放進列表中,
讓程式碼加精簡:
scores = [20,30,50,60,25,70]
new_scores = [s+100 - max(scores) for s in scores]
一行就解決了所有事情,
使用列表生成式的話,
可以大大簡化python代碼,
卻又不失可讀性。
>>> [x * x for x in range(1, 11)]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
>>> s="Hello"
>>> [c for c in s] #亦可直接寫 list(s)
['H', 'e', 'l', 'l', 'o']
>>> [m + n for m in 'abc' for n in '123']
['a1', 'a2', 'a3', 'b1', 'b2', 'b3', 'c1', 'c2', 'c3']
簡單吧?
凡是運用可迭代容器的原始資料(如: 字串,元組,列表,字典),
要生成一個新列表,
都很適合使用列表生成式。
小明上數學課,
學到了3的倍數判別法,
把一個整數的各個位數和相加,
如果是3的倍數的話,
原來的數也是3的倍數。
例如: 123的各位數字和為 1+2+3=6,
6是3的倍數,所以123也是3的倍數。
由於小明覺得這個判別法實在太神奇了,
因此之後小明看到一個整數,
都會有把整數的各個位數加起來的衝動。
請實作這個函數計算整數的各個位數和:
def sumOfDigit(num):
pass
輸入num是一個整數 (為了方便,假設一定是正整數),
回傳該整數的各位數字和。
想一想,如果單純把num當作數字來操作的話,
要怎麼取得各位數字呢?
好像不是那麼好想。
先想如何取得「個位數字」吧。
數字除以10的餘數即是個位數字。
接下來要取得十位數字,
只要先把數字「向右移一位」再取個位數字即可。
亦即反覆的做對10取餘數、除以10的操作即可。
以 123 這個數字為例:
123 % 10 = 3
123 // 10 = 12
12 % 10 = 2
12 //10 = 1
1 % 10 = 1
1 // 10 =0
把過程中取餘數運算(%) 得到的數 3,2,1相加,
即為各位數字之和。
程式碼如下:
def sumOfDigit(num):
ans=0
while num>0:
ans+=num%10
num//=10
return ans
昨天為大家介紹python中各種不同資料型態的特性,
如果我們將數字想成是一個字串陣列呢?
例如把123的「1」,「2」,「3」都看做是一個字元,
直接把每個字加起來不是很直覺嗎?
我們搭配列表生成式,把數字字元轉換成數字,
程式碼如下:
def sumOfDigit(num):
return sum([int(c) for c in str(num)])
舉幾個數字測試一下結果:
print(sumOfDigit(120)) #3
print(sumOfDigit(35)) #8
print(sumOfDigit(261)) #9
print(sumOfDigit(999)) #27
相信小時候大家都有背過九九乘法表吧?
1*1=1, 1*2=2, 1*3=3, …, 1*9=9,
2*1=2, 2*2=4, 2*3=6, …, 2*9=18,
…
9*1=9, 9*2=18,9*3=27, …, 9*9=81
試著用程式印出一份九九乘法表吧。
要直接印出整張九九乘法表似乎不好想,
如果我們只要印出一列呢?例如印出
3*1 到 3*9 的結果:
>>> [3*i for i in range(1,10)]
[3, 6, 9, 12, 15, 18, 21, 24, 27]
很簡單就可以寫出來。
但由於我們希望把整個算式3*1=3
印出來,
而不是只有印出3
一個數,
我們需要使用字串前綴f
來處理:
>>> [ f"3*{i}={3*i}" for i in range(1,10)]
['3*1=3',
'3*2=6',
'3*3=9',
'3*4=12',
'3*5=15',
'3*6=18',
'3*7=21',
'3*8=24',
'3*9=27']
(由於螢幕空間不夠印出一整列,印出來會換行顯示)
之前在Day9給大家講過一個很簡便的語法,
在字串前面加上前綴f
,
即可很方便的在字串裡面放入變數。
這是在python3.6版本加入的新語法,
在字串前面加上f,f"3*{i}={3*i}"
變會把大括號裡的數當作算式把值計算出來。
單純用字串串接的寫法 "3*"+str(i)+"="+str(3*i)
也可以得到一樣的結果,
但是當變數在字串裡面很分散的話,
用前綴f
的寫法看起來會簡單很多。
既然會印出3*1 到 3*9 的結果了,
那如果改成印4*1 到 4*9 的結果,
也就只是簡單的舉一反三了: [ f"4*{i}={4*i}" for i in range(1,10)]
也就是說,印出整張九九乘法表,
把原來放3
的這個位置用個變數取代就行了。
>>> [[ f"{j}*{i}={j*i}" for i in range(1,10)] for j in range(1,10)]
[['1*1=1',
'1*2=2',
'1*3=3',
'1*4=4',
'1*5=5',
'1*6=6',
'1*7=7',
'1*8=8',
'1*9=9'],
['2*1=2',
'2*2=4',
'2*3=6',
'2*4=8',
'2*5=10',
'2*6=12',
'2*7=14',
'2*8=16',
'2*9=18'],
['3*1=3',
'3*2=6',
'3*3=9',
'3*4=12',
'3*5=15',
'3*6=18',
'3*7=21',
'3*8=24',
'3*9=27'],
…(中間省略部分結果)
['8*1=8',
'8*2=16',
'8*3=24',
'8*4=32',
'8*5=40',
'8*6=48',
'8*7=56',
'8*8=64',
'8*9=72'],
['9*1=9',
'9*2=18',
'9*3=27',
'9*4=36',
'9*5=45',
'9*6=54',
'9*7=63',
'9*8=72',
'9*9=81']]
但是目前有個缺點,
全部都換行顯示,
並不是很方便閱讀,
我們可以搭配在Day6講解過的' '.join()
語法,把列表內字串串起來:
for j in range(1,10):
print(' '.join([ f"{j}*{i}={j*i}" for i in range(1,10)]))
結果為:
1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 1*6=6 1*7=7 1*8=8 1*9=9
2*1=2 2*2=4 2*3=6 2*4=8 2*5=10 2*6=12 2*7=14 2*8=16 2*9=18
3*1=3 3*2=6 3*3=9 3*4=12 3*5=15 3*6=18 3*7=21 3*8=24 3*9=27
4*1=4 4*2=8 4*3=12 4*4=16 4*5=20 4*6=24 4*7=28 4*8=32 4*9=36
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 5*6=30 5*7=35 5*8=40 5*9=45
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 6*7=42 6*8=48 6*9=54
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 7*8=56 7*9=63
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 8*9=72
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
但是因為計算結果有的是一位數,有的是兩位數,
這樣印出來沒有對齊,
我們可以把列印的語法改成f"{j}*{i}={j*i:2d}"
,
在大括號中加入冒號,指定我們想要的格式,
例如{j*i:2d}
表示把計算的結果當作2位數整數來印,
(你可以參考官方文檔對輸出格式的說明)
修改如下:
for j in range(1,10):
print(' '.join([ f"{j}*{i}={j*i:2d}" for i in range(1,10)]))
結果為:
1*1= 1 1*2= 2 1*3= 3 1*4= 4 1*5= 5 1*6= 6 1*7= 7 1*8= 8 1*9= 9
2*1= 2 2*2= 4 2*3= 6 2*4= 8 2*5=10 2*6=12 2*7=14 2*8=16 2*9=18
3*1= 3 3*2= 6 3*3= 9 3*4=12 3*5=15 3*6=18 3*7=21 3*8=24 3*9=27
4*1= 4 4*2= 8 4*3=12 4*4=16 4*5=20 4*6=24 4*7=28 4*8=32 4*9=36
5*1= 5 5*2=10 5*3=15 5*4=20 5*5=25 5*6=30 5*7=35 5*8=40 5*9=45
6*1= 6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 6*7=42 6*8=48 6*9=54
7*1= 7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 7*8=56 7*9=63
8*1= 8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 8*9=72
9*1= 9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
哇,沒想到短短程式碼竟可以這麼整齊的印出九九乘法表呢。
今天就先講到這裡吧,明天會繼續講講列表生成式更進階的運用。
又到了每日一次的課後練習時間了,
底下的習題很簡單,
或許你沒學過python而用你原本會的語言都解的出來,
不過試試用python簡潔的風格做出來吧,
歡迎於留言區討論想法,解答於次日公佈。
小櫻是一位小說家,她覺得每天堅持做一件事很有意義,
也決定參加自訂「鐵人賽」,
她給自己的目標是連續N週每天創作小說。
用一個大小為N列七個元素的整數二維列表W,
記錄小櫻每天創作的字數。
請實作findMin(N, W)
這個函數,
回傳小櫻每天最少創作了多少個字。
以下給兩個範例:
範例一:
input: N=1,W=[[819, 958, 508, 969, 565, 800, 703]]
output: 508 (7個整數中最小的是508)
範例二:
input: N=2, W=[[740, 516, 725, 718, 861, 634, 723],
[914, 747, 580, 593, 722, 877, 595]]
output: 516 (14個整數中最小的是516)
提示: 其實這個問題就是要求一個二維陣列的最小值,
如果如下直接呼叫內建函數min()可以嗎?
def findMin(N, W):
return min(W)
不行的話,思考在列表生成式用min()?
def findMin(N, W):
return min([a for ls in W for a in ls]))